package com.rr.mymoney.jsf;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedProperty;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.primefaces.event.CellEditEvent;
import org.primefaces.model.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;

import com.rr.mymoney.bo.AccountBo;
import com.rr.mymoney.bo.CategoryBo;
import com.rr.mymoney.bo.CurrencyBo;
import com.rr.mymoney.bo.TransactionBo;
import com.rr.mymoney.jsf.util.FilterUtils;
import com.rr.mymoney.model.Account;
import com.rr.mymoney.model.Category;
import com.rr.mymoney.model.Currency;
import com.rr.mymoney.model.Transaction;
import com.rr.mymoney.model.User;
import com.rr.mymoney.model.util.ModelFactory;
import com.rr.mymoney.security.SecurityUtils;
import com.rr.mymoney.upload.ImportData;
import com.rr.mymoney.upload.parser.GenericFileUploadParser;
import com.rr.mymoney.utils.DateUtils;
import com.rr.mymoney.utils.Filter;

// Spring instantiates the object, so there is no use for JSF annotations
@Controller
@Scope("view")
public class TransactionsController extends AbstractController {

    private static final long serialVersionUID = -4160268626176404677L;

    private static transient Logger log = Logger.getLogger(TransactionsController.class);

    private transient TransactionBo transactionBo;
    private transient AccountBo accountBo;
    private transient CategoryBo categoryBo;
    private transient CurrencyBo currencyBo;

    private List<Transaction> transactions;
    private Transaction selectedTransaction;
    private Transaction transaction = ModelFactory.newTransaction();
    private Account account = new Account();
    private UploadedFile file;

    @ManagedProperty(value = "#{filter}")
    private transient Filter filter = FilterUtils.getFilterFromSession("transactionsFilter");

    private Float balance;

    @Autowired
    public void setTransactionBo(TransactionBo transactionBo) {
	this.transactionBo = transactionBo;
    }

    @Autowired
    public void setAccountBo(AccountBo accountBo) {
	this.accountBo = accountBo;
    }

    @Autowired
    public void setCategoryBo(CategoryBo categoryBo) {
	this.categoryBo = categoryBo;
    }

    @Autowired
    public void setCurrencyBo(CurrencyBo currencyBo) {
	this.currencyBo = currencyBo;
    }

    public void init(ComponentSystemEvent event) throws AbortProcessingException {
    }

    public void filter() {
	log.debug(filter);
	FilterUtils.saveFilterToSession(filter, "transactionsFilter");
	transactions = transactionBo.getFiltered(getLoggedUser().getId(), filter.getYearMonth() != null ? DateUtils.getMonth(filter.getYearMonth())
		: 3, filter.getYearMonth() != null ? DateUtils.getYear(filter.getYearMonth()) : 2011, null, filter.getCategory() != null
		&& filter.getCategory().getId() != null && filter.getCategory().getId() > 0 ? filter.getCategory().getId() : null,
		filter.getAccount() != null && filter.getAccount().getId() != null && filter.getAccount().getId() > 0 ? filter.getAccount().getId()
			: null, filter.getCurrency() != null && !StringUtils.isEmpty(filter.getCurrency().getId()) ? filter.getCurrency().getId()
			: null);
	balance = 0f;
	for (Transaction transaction : transactions) {
	    balance += transaction.getValue();
	}
    }

    public List<Transaction> getTransactions() {
	if (transactions == null) {
	    filter();
	}
	return transactions;
    }

    public Transaction getSelectedTransaction() {
	return selectedTransaction;
    }

    public void setSelectedTransaction(Transaction selectedTransaction) {
	this.selectedTransaction = selectedTransaction;
	if (this.selectedTransaction != null && this.selectedTransaction.getCategory() == null) {
	    this.selectedTransaction.setCategory(new Category());
	}
    }

    public Transaction getTransaction() {
	return transaction;
    }

    public void setTransaction(Transaction transaction) {
	this.transaction = transaction;
    }

    public Account getAccount() {
	return account;
    }

    public void setAccount(Account account) {
	this.account = account;
    }

    public UploadedFile getFile() {
	return file;
    }

    public void setFile(UploadedFile file) {
	this.file = file;
    }

    public void saveTransaction() {
	transaction.setUser(SecurityUtils.getLoggedUser());
	log.debug("save transaction: " + transaction.toString());
	transactionBo.saveOrUpdate(transaction);
	transactions = null;
	getTransactions();
    }

    public void updateTransaction() {
	log.debug("update transaction: " + selectedTransaction.toString());
	transactionBo.saveOrUpdate(selectedTransaction);
	transactions = null;
	getTransactions();
    }

    public void deleteTransaction() {
	try {
	    log.debug("delete transaction: " + selectedTransaction.toString());
	    transactionBo.delete(selectedTransaction);
	    transactions = null;
	    getTransactions();
	} catch (Exception e) {
	    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Delete", e.getMessage());
	    FacesContext.getCurrentInstance().addMessage(null, message);
	    log.error("Error on delete: ", e);
	}
    }

    public Map<String, String> getPosibleCurrencies() {
	Map<String, String> retVal = new HashMap<String, String>();
	for (Currency currency : currencyBo.getAll()) {
	    retVal.put(currency.getLabel(), currency.getId());
	}
	return retVal;
    }

    public void handleFileUpload() {
	FacesMessage msg = new FacesMessage();
	if (file != null) {
	    msg = new FacesMessage("Succesful", file.getFileName() + " uploaded.");
	    try {
		GenericFileUploadParser genericParser = new GenericFileUploadParser();
		ImportData importData = genericParser.parse(file.getInputstream());
		if (importData != null) {
		    log.debug(importData);
		    account = accountBo.get(account.getId());
		    transactionBo.importTransactionData(importData, account);
		    msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Succesful", importData.getItems().size() + " transactions uploaded, "
			    + (importData.getItems().size() - importData.getDoubled()) + " imported.");
		    transactions = null;
		} else {
		    log.debug("No transaction data to import.");
		    msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Succesful", "No transaction data to import.");
		}
	    } catch (Exception e) {
		log.error("", e);
		msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Impor failed.");
	    }
	} else {
	    log.debug("No file to upload.");
	}
	FacesContext.getCurrentInstance().addMessage("message", msg);
	log.debug(msg.getDetail());
    }

    public Map<String, String> getAccounts() {
	Map<String, String> retVal = new HashMap<String, String>();
	User loggedUser = SecurityUtils.getLoggedUser();
	if (loggedUser != null) {
	    List<Account> accounts = accountBo.getByUserId(loggedUser.getId());
	    for (Account account : accounts) {
		retVal.put(account.getName(), account.getId() + "");
	    }
	}
	return retVal;
    }

    public Map<String, String> getCategories() {
	Map<String, String> retVal = new LinkedHashMap<String, String>();
	User loggedUser = SecurityUtils.getLoggedUser();
	if (loggedUser != null) {
	    List<Category> categories = categoryBo.getByUserId(loggedUser.getId(), true);
	    for (Category category : categories) {
		retVal.put(category.getName(), category.getId() + "");
		List<Category> subcategories = category.getSubCategories();
		if (!CollectionUtils.isEmpty(subcategories)) {
		    for (Category subcategory : subcategories) {
			retVal.put("&nbsp;&nbsp;&nbsp;&nbsp;" + subcategory.getName(), subcategory.getId() + "");
		    }
		}
	    }
	}
	return retVal;
    }

    public Filter getFilter() {
	return filter;
    }

    public void setFilter(Filter filter) {
	this.filter = filter;
    }

    public void updateToPreviousMonth() {
	int prevYearMonth = DateUtils.getPreviousMonth(filter.getYear() * 100 + filter.getMonth());
	filter.setYear(DateUtils.getYear(prevYearMonth));
	filter.setMonth(DateUtils.getMonth(prevYearMonth));
    }

    public void updateToNextMonth() {
	int prevYearMonth = DateUtils.getNextMonth(filter.getYear() * 100 + filter.getMonth());
	filter.setYear(DateUtils.getYear(prevYearMonth));
	filter.setMonth(DateUtils.getMonth(prevYearMonth));
    }

    public Float getBalance() {
	return balance;
    }
    
    public String getMonthDisplay() {
	DateFormat df = new SimpleDateFormat("MMMM YYYY");
	return df.format(DateUtils.getStartDate(filter.getYear(), filter.getMonth()));
    }
    
    public void setMonthDisplay(String monthDisplay) {
    }
    
    @SuppressWarnings("unused")
    public void onCellEdit(CellEditEvent event) {
	Object oldValue = event.getOldValue();
	Object newValue = event.getNewValue();

	Transaction t = getTransactions().get(event.getRowIndex());
	if (t.getCategory() != null && t.getCategory().getId() == null) {
	    t.setCategory(null);
	}
	transactionBo.saveOrUpdate(t);
	t.getCategory();
    }
}
